"""
/*
 * Copyright 2011 OpenWAF.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
 """
"""
This file contains code related to Controller/View pair
"""
import sys
from HTMLParser import WAFHTMLParser
from HTMLParser import HTMLViewNode
from Analyzer import SClass, SHelper, SGlobal
from ConfigReader import WAFConfig
import Java2js
from JavaLangAST import Literal
from Java2js import ExternCreate
def markRefsForVC(view_file,cls):
    try:
        hv = WAFHTMLParser(view_file, False)
        markObjectCreationCode(hv.rootNode,cls)
    except Exception as e:
        print "Exception in in processing view file:",view_file
        print str(e)
        raise e
        
        
    
def processControllerViewPair(view_file,cls):
    hv = WAFHTMLParser(view_file, False)
    ids = {}
    id_list=[0]
    id_list.append({})    
    getIds(hv.rootNode,ids,id_list)
    ccode=[]
    getObjectCreationCode(hv.rootNode,cls,ccode,id_list)
    cons=cls.getConstructor([],None)
    fcode=[]
    #fcode.append("\nfunction __init_openwaf_view(){")
    
    Literal.strings={}
    Literal.index=0
    SGlobal.compiling_controller=True
    old_static_field_lines=SGlobal.static_field_lines
    old_static_code_calls=SGlobal.static_code_calls
    SGlobal.static_field_lines=[]
    SGlobal.static_code_calls=[]
    old_ex_creates=ExternCreate.instances
    old_ex_mds=ExternCreate.md5s
    ExternCreate.instances=[]
    ExternCreate.md5s={}
    
    
    fcode.append(WAFConfig.getWAFRootObjectName()+"._cb.d=document.createElement('div');");    
    fcode.append("var __ce=null")
    fcode.append("var __ms=null")
    fcode.append("\n".join(ccode))
    fcode.append("}")
    fcode.append("var $app;")
    fcode.append("function _init_openwaf_app(){")
    fcode.append(Java2js.SA.checkType(cls,True))
    line = "new " + cls.getPrototype() + "()"
    fcode.append(["$app="+line+";"])
    fcode.append("__init_openwaf_view.call($app);")
    adjusted_folder=""
    package=cls.getPackage()
    if package!=None:
        pparts=package.split(".")
        adjusted_folder="../"*(len(pparts)-1)
    pu=adjusted_folder+WAFConfig.getProxyURL()
    fcode.append("$app._proxy_url=\""+pu+"\";")
    fcode.append("$app."+cons.getJSName()+"();")
    fcode.append("}")
    line=""
    if WAFConfig.isStringAggregateEnabled()==True:
        line=WAFConfig.getWAFRootObjectName() + ".$s=["
        count=0
        str_ar=[]
        l=len(Literal.strings)
        for i in range(l):            
            for key in Literal.strings:
                i=Literal.strings[key]
                if i==count:
                    str_ar.append(key)
                    count+=1
                    break        
        line+=",".join(str_ar)
        line+="];"
        
    ffcode=["\nfunction __init_openwaf_view(){",line]+SGlobal.static_field_lines+SGlobal.static_code_calls
    ext = Java2js.ExternCreate.getCode()
    ffcode=ffcode+ext+fcode    
    jcode=Java2js.getFormatedCode(ffcode, 0)
    SGlobal.compiling_controller=False
    ExternCreate.instances=old_ex_creates
    ExternCreate.md5s=old_ex_mds
    SGlobal.static_field_lines=old_static_field_lines
    SGlobal.static_code_calls=old_static_code_calls
    code=[]
    html_node=None
    head_node=None
    body_node=None
    for cn in hv.rootNode.childNodes:
        if cn.name.lower()=="html":
            html_node=cn
            for ccn in cn.childNodes:
                if ccn.name.lower()=="head":
                    head_node=ccn
                elif ccn.name.lower()=="body":
                    body_node=ccn
    if html_node==None:
        html_node=HTMLViewNode()
        html_node.name="html"
    if head_node==None:
        head_node=HTMLViewNode()
        head_node.name="head"
        html_node.childNodes=[head_node]+html_node.childNodes
    if body_node==None:
        body_node=HTMLViewNode()
        body_node.name="body"
        html_node.childNodes.append(body_node)
    adjusted_folder=""
    package=cls.getPackage()
    if package!=None:
        pparts=package.split(".")
        adjusted_folder="../"*(len(pparts)-1)
    for ss in WAFConfig.style_sheets:
        #<link rel="stylesheet" href="openwaf-default.css" type="text/css" media="screen"/>
        tag=HTMLViewNode()
        tag.name="link"
        tag.attributes.append(["rel","stylesheet"])
        tag.attributes.append(["type","text/css"])
        tag.attributes.append(["href",adjusted_folder+ss])
        tag.attributes.append(["media","screen"])
        head_node.childNodes.append(tag)
        tag=HTMLViewNode()
        tag.nodeType=HTMLViewNode.NODE_DATA
        tag.innerHTML="\n"
        head_node.childNodes.append(tag)
    script_tag=HTMLViewNode()
    script_tag.name="script"
    script_tag.attributes.append(["type","text/javascript"])
        
    script_tag.attributes.append(["src",adjusted_folder+WAFConfig.getCompiledJSFileName()])
    head_node.childNodes.append(script_tag)
    empty_node=HTMLViewNode()
    empty_node.nodeType=HTMLViewNode.NODE_DATA
    empty_node.innerHTML="\n"
    head_node.childNodes.append(empty_node)
    script_tag=HTMLViewNode()
    script_tag.name="script"
    script_tag.attributes.append(["type","text/javascript"])
    data_node=HTMLViewNode()
    data_node.nodeType=HTMLViewNode.NODE_DATA
    data_node.innerHTML=jcode
    script_tag.childNodes.append(data_node)
    head_node.childNodes.append(script_tag)
    added_init=False
    for a in body_node.attributes:
        name=a[0]        
        if name=="onload":
            a[1]=removeQuote(a[1])+";_init_openwaf_app();"
            added_init=True
            break
    if added_init==False:
        body_node.attributes.append(["onload","_init_openwaf_app()"])         
    getHTMLViewCode(hv.rootNode,code)
    return "".join(code)
def removeQuote(a):
    if a.startswith("\""):
        return a[1:-1]
    return a
        
def containsId(a):
    for i in a:
        if i[0]=="id" or i[0]=="ID":return True           
    return False
def containsIdGet(a):
    for i in a:
        if i[0]=="id" or i[0]=="ID":  
            n=i[1]
            if n==None:
                raise Exception("id is none")
            return removeQuote(n)
    print a
    raise Exception("id not found")

def getIds(rootNode, ids,id_count):
    if rootNode.nodeType in [HTMLViewNode.NODE_COMMENT,HTMLViewNode.NODE_DECL]:return
    for ch in rootNode.childNodes:
        getIds(ch, ids,id_count) 
    if containsId(rootNode.attributes):
        value = containsIdGet(rootNode.attributes)        
        if ids.has_key(value):
            raise Exception("Exception duplicate id " + value)
        ids[value] = rootNode
        id_count[1][rootNode]="this."+value
        rootNode.id=value
    elif rootNode.name.startswith("WAF:"):
        i="__wid_"+str(id_count[0])
        id_count[0]+=1
        rootNode.attributes.append(["id",i])
        id_count[1][rootNode]=i
        ids[i] = rootNode
        rootNode.id=i
    
        
def getHTMLViewCode(rnode, code):
    if rnode.nodeType in [HTMLViewNode.NODE_COMMENT,HTMLViewNode.NODE_DECL]:
        code.append(rnode.innerHTML)
        return
    if rnode.nodeType==HTMLViewNode.NODE_DATA:
        code.append(rnode.innerHTML)
        return
    if len(rnode.name.strip()) != 0:
        s=""
        if rnode.name.startswith("WAF:"):
            s ="<div"
        else:
            s = "<" + rnode.name
        for atr in rnode.attributes:
            name=atr[0]
            value=atr[1]
            if rnode.name.startswith("WAF:") and (not name.lower()=="id"):continue
            s += " " + name
            if value != None:
                s += "=\"" + removeQuote(value) + "\""
        if len(rnode.childNodes) > 0:
            s += ">"
            code.append(s)
        else:
            if rnode.name.startswith("WAF:"):
                s+="></div>"
            elif rnode.name.lower() in ["div" ,"script","span","ol","ul","textarea","iframe","table" ]:
                s += "></"+rnode.name +">"  #need to investigate might be some bug in browser in rendering this
            else:
                s += "/>"
            code.append(s)
            return
    
    if (len(rnode.name.strip()) != 0):
        if rnode.name.startswith("WAF:"):
            for cnode in rnode.childNodes:
                if cnode.nodeType!=HTMLViewNode.NODE_ELEMENT:
                    getHTMLViewCode(cnode, code)
                    continue
                code.append("<div name=\""+cnode.name.strip()+"\">")
                for cc in cnode.childNodes:
                    getHTMLViewCode(cc, code)
                code.append("</div>")
            code.append("</div>")
            return 
    for cnode in rnode.childNodes:
        getHTMLViewCode(cnode, code)
    if len(rnode.name.strip()) != 0:
        code.append("</" + rnode.name + ">")
def addMethodRef(method,mid):
    if id not in method.refs:
        method.refs.append(mid)
    if not SGlobal.method_refs.has_key(method.mid):
        SGlobal.method_refs[method.mid]=method

def markObjectCreationCode(rnode,cls):
    cur_method=cls.getConstructor([],None)
    markCreationCode(rnode, cls,cur_method)
def markCreationCode(rnode,cls,cur_method):
    if rnode.nodeType in [HTMLViewNode.NODE_COMMENT,HTMLViewNode.NODE_DECL]:return
    if rnode.name.startswith("WAF:"):
        for cn in rnode.childNodes:
            for ccn in cn.childNodes:
                markCreationCode(ccn,cls,cur_method)
    else:
        for ch in rnode.childNodes:
            markCreationCode(ch,cls,cur_method)
    if not rnode.name.startswith("WAF:"):return
    cname=rnode.name[4:]
    control_cls=SHelper.getClassOnNameFromImported(cname, cls)
    if control_cls==None:
        raise Exception("Class not found with name ",cname)
    cons=control_cls.getConstructor([],None)
    if cons==None:
        raise Exception("Class dont have default constructor "+control_cls.fullname)
    #if WAFConfig.isRemoveUnusedCode():
    addMethodRef(cur_method,cons.mid)
    for atr in rnode.attributes:
        name=atr[0]
        if name.lower()=="id":continue
        name="set"+name.capitalize()
        m=control_cls.getMethodWithThisAccess(name,[SGlobal.stringclass.mytype],False, None,cls)
        if m!=None:
            #if WAFConfig.isRemoveUnusedCode():
            addMethodRef(cur_method,m.method.mid)

    for cn in rnode.childNodes:
        if cn.nodeType!=HTMLViewNode.NODE_ELEMENT:continue        
        name=cn.name
        #count=countNonEmptyChilds(cn.childNodes)
        #t=SHelper.getClassOnFullName("com.openwaf.client.dom.Element").mytype
        types=getMethodArgsTypes(cn.childNodes, cls)
        #for i in range(count):
        #    types.append(t)
        m=control_cls.getMethodWithThisAccess(name,types,False,None,cls)
        if m==None:
            raise Exception(cls.fullname+"(View Error)Method not found "+name+" for class "+control_cls.fullname)
        #if WAFConfig.isRemoveUnusedCode():
        addMethodRef(cur_method,m.method.mid)
def getMethodArgsTypes(nodes,cls):
    if nodes==None:return []
    types=[]
    t=SHelper.getClassOnFullName("com.openwaf.client.dom.Element").mytype
    for cn in nodes:
        if cn.nodeType!=HTMLViewNode.NODE_ELEMENT:continue
        if cn.name.startswith("WAF:"):
            name=cn.name[4:]
            a=SHelper.getClassOnNameFromImported(name, cls)
            if a==None:
                raise Exception("Class not found in view "+name)
            types.append(a.mytype)            
        else:
            types.append(t)
            
    return types
        

def getObjectCreationCode(rnode,cls,code,id_list):    
    cur_method=cls.getConstructor([],None)
    getCreationCode(rnode, cls, code,cur_method,id_list)
def getCreationCode(rnode,cls,code,cur_method,id_list):
    if rnode.nodeType in [HTMLViewNode.NODE_COMMENT,HTMLViewNode.NODE_DECL]:return
    if rnode.name.startswith("WAF:"):
        for cn in rnode.childNodes:
            for ccn in cn.childNodes:
                getCreationCode(ccn,cls,code,cur_method,id_list)
    else:
        for ch in rnode.childNodes:
            getCreationCode(ch,cls,code,cur_method,id_list)
    if not rnode.name.startswith("WAF:"):return          
    value = containsIdGet(rnode.attributes)
    cname=rnode.name[4:]
    control_cls=SHelper.getClassOnNameFromImported(cname, cls)
    if control_cls==None:
        raise Exception("Class not found with name ",cname)
    cons=control_cls.getConstructor([],None)
    if cons==None:
        raise Exception("Class dont have default constructor "+control_cls.fullname)
    #if WAFConfig.isRemoveUnusedCode():
    #addMethodRef(cur_method,cons.mid)

    line = "(new " + control_cls.getPrototype() + "())." + cons.getJSName() + "()"
    varname=value;
    if value.startswith("__wid_"):
        code.append("var "+value+"="+line+";")
    else:
        field=cls.getField(value)
        if field==None:
            raise Exception("Field is not declared in class  "+cls.fullname+" "+value)
        code.append("this."+field.getJSName()+"="+line+";")
        varname="this."+field.getJSName()
    for a in rnode.attributes:
        atr=a[0]        
        if atr.lower()=="id":continue
        if a[1]==None:continue
        atr_value=removeQuote(a[1])
        name="set"+atr[0:1].upper()+atr[1:]
        m=control_cls.getMethodWithThisAccess(name,[SGlobal.stringclass.mytype],False, None,cls)
        if m==None:
            #print "Methd not found",name
            code.append( WAFConfig.getWAFRootObjectName()+"._cb.sa("+varname+".elm,\""+atr+"\",\""+atr_value+"\");")            
        else:
            code.append(varname+"."+m.method.getJSName()+"(\""+atr_value+"\");")
            #if WAFConfig.isRemoveUnusedCode():
            #addMethodRef(cur_method,m.method.mid)
    code.append("__ce=document.getElementById(\""+value+"\");")
    mcode=[]
    mcode.append("__ms="+WAFConfig.getWAFRootObjectName()+"._cb.ge(__ce);")
    mcount=0
    for cn in rnode.childNodes:
        if cn.nodeType!=HTMLViewNode.NODE_ELEMENT:continue
        
        name=cn.name
        childs=getNonEmptyChilds(cn)
        count=len(childs)
        t=SHelper.getClassOnFullName("com.openwaf.client.dom.Element").mytype
        #types=[]
        #for i in range(count):
        #    types.append(t)
        types=getMethodArgsTypes(cn.childNodes, cls)
        m=control_cls.getMethodWithThisAccess(name,types,False,None,cls)
        if m==None:
            raise Exception(cls.fullname+"(View Error)Method not found "+name+" for class "+control_cls.fullname)
        #if WAFConfig.isRemoveUnusedCode():
        #addMethodRef(cur_method,m.method.mid)
        mcode.append("__a="+WAFConfig.getWAFRootObjectName()+"._cb.ge(__ms["+str(mcount)+"]);")
        line=varname+"."+m.method.getJSName()+"("
        pars=[]
        for i in range(count):
            if types[i]==t:
                pars.append("__a["+str(i)+"]")
            else:
                if id_list[1].has_key(childs[i]):                    
                    if containsId(childs[i].attributes):
                        fname=containsIdGet(childs[i].attributes)                        
                        if not fname.startswith("__wid"):                                                    
                            field=cls.getField(fname)
                            pars.append("this."+field.getJSName())
                        else:
                            pars.append(fname)
                    else:                    
                        pars.append(id_list[1][childs[i]])
                else:
                    raise Exception("UnExpected Some went wrong please report this.")                
        line=line+",".join(pars)+");"
        mcode.append(line)
        mcount+=1
    if len(mcode)>1:
        code.append("\n".join(mcode))              
    code.append(WAFConfig.getWAFRootObjectName()+"._cb.rn(__ce,"+varname+".elm);")
def countNonEmptyChilds(rnode):
    count=0
    for ch in rnode.childNodes:
        if ch.nodeType==HTMLViewNode.NODE_ELEMENT:
            count+=1
        elif ch.nodeType==HTMLViewNode.NODE_DATA:
            if ch.innerHTML!=None and len(ch.innerHTML.strip())>0:
                count+=1
    return count
def getNonEmptyChilds(rnode):
    cs=[]
    for ch in rnode.childNodes:
        if ch.nodeType==HTMLViewNode.NODE_ELEMENT:
            cs.append(ch)
        elif ch.nodeType==HTMLViewNode.NODE_DATA:
            if ch.innerHTML!=None and len(ch.innerHTML.strip())>0:
                cs.append(ch)
    return cs
    
                                          

if __name__=="__main__":
    processControllerViewPair(sys.argv[1], sys.argv[2])